/*******************************************************************************
 *                                    ZLG
 *                         ----------------------------
 *                         innovating embedded platform
 *
 * Copyright (c) 2001-present Guangzhou ZHIYUAN Electronics Co., Ltd.
 * All rights reserved.
 *
 * Contact information:
 * web site:    https://www.zlg.cn
 *******************************************************************************/
/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "driver/usb/hpm6e00_usb.h"

/*******************************************************************************
 * Marco operate
 ******************************************************************************/
#define USB_PHY_INIT_DELAY_COUNT  (16)

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief USB 寄存器检查函数
 *
 * \param[in] p_usb_reg 相关 USB 寄存器
 *
 * \retval 成功返回 0
 */
int usb_reg_chk(hpm_usb_reg_t *p_usb_reg){
    if ((p_usb_reg == NULL) || ((p_usb_reg != HPM_USB0))) {
        return -EINVAL;
    }
    return 0;
}

/**
 * \brief 初始化 USB PHY
 *
 * \param[in] p_usb_reg 相关 USB 寄存器
 *
 * \retval 成功返回 0
 */
int usb_phy_init(hpm_usb_reg_t *p_usb_reg){
    uint32_t regval, time_out = 5000;
    int      i, ret;

    ret = usb_reg_chk(p_usb_reg);
    if (ret != 0) {
        return ret;
    }

    regval = READ_REG32(p_usb_reg->otg_ctrl0);
    regval |= USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK;
    regval &= ~USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK;
    WRITE_REG32(p_usb_reg->otg_ctrl0, regval);

    regval = READ_REG32(p_usb_reg->phy_ctrl1);
    regval &= ~USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK;
    WRITE_REG32(p_usb_reg->phy_ctrl1, regval);
    do {
        regval = READ_REG32(p_usb_reg->otg_ctrl0);
    } while ((--time_out) && (!(regval & (1 << USB_OTG_CTRL0_OTG_UTMI_RESET_SW_POS))));
    if (time_out == 0) {
        return -ETIME;
    }

    regval  = READ_REG32(p_usb_reg->otg_ctrl0);
    regval |= USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK;
    WRITE_REG32(p_usb_reg->otg_ctrl0, regval);

    for (i = 0; i < USB_PHY_INIT_DELAY_COUNT; i++) {
        /* 用于延时 */
        WRITE_REG32(p_usb_reg->phy_ctrl0, (0 << 25));
    }

    regval = READ_REG32(p_usb_reg->otg_ctrl0);
    regval &= ~USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK;
    WRITE_REG32(p_usb_reg->otg_ctrl0, regval);

    /* OTG UTMI 时钟检测 */
    regval = READ_REG32(p_usb_reg->phy_status);
    /* 写 1 清除有效状态 */
    regval |= USB_PHY_STATUS_UTMI_CLK_VALID_MASK;
    WRITE_REG32(p_usb_reg->phy_status, regval);

    time_out = 50000;
    do {
        regval = READ_REG32(p_usb_reg->phy_status);
    } while ((--time_out) && (!(regval & (1 << USB_PHY_STATUS_UTMI_CLK_VALID_POS))));
    if (time_out == 0) {
        return -ETIME;
    }

    SET_REG32_BIT(p_usb_reg->phy_ctrl1, USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK);
    SET_REG32_BIT(p_usb_reg->phy_ctrl1, USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK);

//    /* HIGH 需要选则并行信号 */
//    regval = READ_REG32(p_usb_reg->portsc1);
//    regval &= ~(1 << 29);
//    WRITE_REG32(p_usb_reg->portsc1, regval);

    return 0;
}

/**
 * \brief USB PHY 反初始化
 *
 * \param[in] p_usb_reg 相关 USB 寄存器
 *
 * \retval 成功返回 0
 */
int usb_phy_deinit(hpm_usb_reg_t *p_usb_reg){
    uint32_t regval;
    int      ret;

    ret = usb_reg_chk(p_usb_reg);
    if (ret != 0) {
        return ret;
    }
    regval = READ_REG32(p_usb_reg->otg_ctrl0);
    regval |= USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK;
    WRITE_REG32(p_usb_reg->otg_ctrl0, regval);

    regval = READ_REG32(p_usb_reg->otg_ctrl0);
    regval &= ~USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK;
    WRITE_REG32(p_usb_reg->otg_ctrl0, regval);

    regval = READ_REG32(p_usb_reg->phy_ctrl1);
    regval &= ~USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK;
    WRITE_REG32(p_usb_reg->phy_ctrl1, regval);

    regval = READ_REG32(p_usb_reg->phy_ctrl1);
    regval &= ~USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK;
    WRITE_REG32(p_usb_reg->phy_ctrl1, regval);

    return 0;
}
